/** @format */

/**
 * @description:工具类---单元格合并
 * @author:ptt
 * @date:2023/12/07
 */

/*@desc: 合并行(合并垂直单元格),会改变传入的数组data
 *@param:  data【Array】
            描述：表格数据
            示例： [  
                    {$index:1,name:'富士苹果',origin:'日本',num:'1'},
                    {$index:2,name:'富士苹果',origin:'辽宁',num:'4'},
                    {$index:3,name:'巨峰葡萄',origin:'日本',num:'3'},
                    {$index:4,name:'巨峰葡萄',origin:'日本',num:'6'},
                    {$index:5,name:'草莓',origin:'日本',num:'2'},
                    {$index:6,name:'草莓',origin:'辽宁',num:'14'},
                    {$index:7,name:'草莓',origin:'辽宁',num:'8'},
                  ]
          allHeaderKeys【Array】
            描述：所有的表头字段名
            示例：['$index','name','origin','num']
          mergeKeys【Array】 
            描述：需要合并的表头字段名
            示例：[ '$index','name','origin']
         
 *@demo:
        tableHeader = [{label:'序号',prop:'$index'},{label:'产品',prop:'name'},{label:'产地',prop:'origin'},{label:'库存',prop:'num'}]
        <tr v-for="item in data" :key="Date.now()">
            <td    
                v-for="td in tableHeader"
                :key="td.prop"
                :rowspan="item[td.prop+'_rowspan']">
                {{item[td.prop] || '-'}}
            </td> 
         </tr>
 *@returns: */
export const mergeRow = function (data, allHeaderKeys, mergeKeys) {
    if (!data.length) return;
    // 设置每个单元格的初始rowspan
    data.map(item => {
        allHeaderKeys.forEach(key => {
            item[key + '_rowspan'] = 1;
        });
    });

    // 创建数据容器,用于存放需要合并的单元格对象
    const mergeObj = {};
    mergeKeys.forEach(key => {
        mergeObj[key] = [];
    });

    // 处理需要合并的数据(会改变原数组)
    data.reduce((preV, curV, idx, arr) => {
        mergeKeys.forEach(key => {
            const mergeArr = mergeObj[key];
            // 存放初始值
            if (idx == 1) {
                mergeArr.push({
                    name: preV[key],
                    index: 0 // 分类项的第一个索引值
                });
            }
            const len = mergeArr.length;
            // 判断上一个存放的对象是否等于当前对象
            const isSame = mergeArr[len - 1].name == curV[key];
            // 若等同则将当前对象合并到上一个对象
            if (isSame) {
                arr[mergeArr[len - 1].index][key + '_rowspan'] += 1;
                arr[idx][key + '_rowspan'] -= 1;
            }
            // 若不等同则新增当前对象到数据容器
            else {
                mergeArr.push({
                    name: curV[key],
                    index: idx
                });
            }
        });
    });
};

/*@desc: 参照已合并的列进行合并（需要先执行mergeRow,注意mergeRow中的mergeKeys是包括referenceKey的,但referenceMergeKeys中的key不得出现在mergeKeys内 ）,会改变传入的数组data
         *@param:  
                  data【Array】
                    描述：表格数据
                    示例： [  
                            {$index:1,name:'富士苹果',origin:'日本',num:'1'},
                            {$index:2,name:'富士苹果',origin:'辽宁',num:'4'},
                            {$index:3,name:'巨峰葡萄',origin:'日本',num:'3'},
                            {$index:4,name:'巨峰葡萄',origin:'日本',num:'6'},
                            {$index:5,name:'草莓',origin:'日本',num:'2'},
                            {$index:6,name:'草莓',origin:'辽宁',num:'14'},
                            {$index:7,name:'草莓',origin:'辽宁',num:'8'},
                          ]
                  referenceKey【String】 
                    描述：被参考合并的表头字段,配合referenceMergeKeys使用
                    示例：'name'
                  referenceMergeKeys【Array】 
                    描述：参考referenceKey的合并方式进行合并的字段。
                    示例：[ '$index','origin']
                  setReferIndex【Boolean】 
                    描述：是否参考referenceKey设置索引。
                    示例：默认flase
         *@attention: $index会被识别为表格索引,如果referenceMergeKeys包含了$index,则会根据referenceKey合并后的数量作为索引
         *@returns:   */
export const compareMerge = function (
    data,
    referenceKey,
    referenceMergeKeys,
    setReferIndex
) {
    if (!data.length) return;
    // 判断参数是否传递有误
    for (let i = 0; i < data.length; i++) {
        for (let j = 0; j < referenceMergeKeys.length; j++) {
            if (!data[i][referenceMergeKeys[j] + '_rowspan']) {
                throw new Error(
                    'mergeRow方法的第三个入参中不得包含' + referenceMergeKeys[j]
                );
            }
        }
    }
    // 组装 referenceKey数据,将有合并项数据的referenceKey_rowspan放入数组
    const referenceList = data
        .map((item, index) => {
            let res = '';
            if (item[referenceKey + '_rowspan']) {
                res = {
                    index: index,
                    name: item[referenceKey],
                    rowspan: item[referenceKey + '_rowspan']
                };
            }
            return res;
        })
        .filter(item => item);

    referenceList.forEach((referItem, referIndex) => {
        // 创建referenceMergeKeys数据容器
        const mergeObj = {};
        referenceMergeKeys.forEach(key => {
            mergeObj[key] = [];
        });
        data.map((item, index) => {
            referenceMergeKeys.forEach(key => {
                // 只筛选要比对的分区数据（区间最小值<当前索引<区间最大值）
                if (
                    referItem.index <= index &&
                    index + 1 <= referItem.index + referItem.rowspan
                ) {
                    // 判断当前分区内的key单元格，是否全部一样
                    const mergeKeyList = [
                        ...new Set(
                            data
                                .slice(item.index, item.index + item.rowspan)
                                .map(item => item[key])
                                .filter(
                                    item =>
                                        !['', '-', null, undefined].includes(
                                            item
                                        )
                                )
                        )
                    ];
                    const isAllSame = mergeKeyList.length == 1;
                    // 一样，则复用分区对应的rowspan
                    if (isAllSame) {
                        item[key + '_rowspan'] =
                            item[referenceKey + '_rowspan'];
                    }
                    // 不一样,则合并此分区里面的同类项
                    else {
                        // 如果是索引
                        if (key === '$index') {
                            // 索引的合并方式参考referenceKey
                            item[key + '_rowspan'] =
                                item[referenceKey + '_rowspan'];
                            if (setReferIndex) {
                                // 同时改变索引值为referenceKey的行数(从1开始)
                                item[key] = referIndex + 1;
                            }
                        } else {
                            // 处理第一次数据
                            if (!mergeObj[key].length) {
                                mergeObj[key].push({
                                    name: item[key],
                                    index: index // 指向数据源的索引
                                });
                            } else {
                                // 当前数据是否已经存在于数据容器中
                                const len = mergeObj[key].length;
                                const isExist =
                                    mergeObj[key][len - 1].name == item[key];
                                // 如果存在
                                if (isExist) {
                                    // 取已存数据的索引,在原始数据中增加rowspan
                                    data[mergeObj[key][len - 1].index][
                                        key + '_rowspan'
                                    ]++;
                                    // 同时减少当前索引对应的rowspan
                                    item[key + '_rowspan']--;
                                }
                                //如果不存在
                                else {
                                    mergeObj[key].push({
                                        name: item[key],
                                        index: index // 指向数据源的索引
                                    });
                                }
                            }
                        }
                    }
                }
            });
        });
    });

    // return data;
};
